/*
 * Files: Human.java
 */

package model;

import model.interfaces.selectable.ControllablePlayer;
import model.interfaces.selectable.SelectableStructure;
import model.interfaces.selectable.SelectableRallyPoint;
import model.interfaces.selectable.SelectableInstance;
import model.interfaces.selectable.SelectableGroup;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Hashtable;

/**
 * Represents a player that is controllable by a human playing the game.
 * Contains information about selectable units and commands available to the
 * user.
 * 
 * 
 * @author Christopher Dudley
 * @author Alex Kagioglu
 */
class Human extends Player implements ControllablePlayer
{
    // deviation from uml
    // player doesn't need this information. human does.
    private List< RallyPoint > rallyPointList;
    
    // deviation from uml
    // player doesn't need this information. human does.private Set<Structure>
    // structureSet;
    private List< Structure > structureList;
    
    private List< SelectableGroup > groups;
    private List< SelectableGroup > armyGroups;
    
    private Type colonists;
    private Type explorers;
    private Type melees;
    private Type ranges;
    private Type towers;
    private Type bases;
    
    private Type rallyPointType;
    private Map< Integer, SelectableGroup > idToArmyGroup;
    
    /**
     * Creates a new human player on a given map at a set starting location with
     * a certain amount of starting resources.
     * 
     * The player starts with 2 explorers, 1 colonist and 1 base at the starting
     * location.
     * 
     * @author alex kagioglu
     * 
     * @param map
     *            the map the player is playing on.
     * @param startPoint
     *            the place on the map that the player starts on.
     * @param startResources
     *            the player's amount of starting resources.
     */
    public Human( GameMap map, Location startLoc, Map< ResourceType, Integer > startResources ) {
        super( map, startLoc, startResources );
        this.rallyPointList = new ArrayList< RallyPoint >();
        this.structureList = new ArrayList< Structure >();
        this.groups = new ArrayList< SelectableGroup >();
        this.armyGroups = new ArrayList< SelectableGroup >();
        
        Group structureGroup = new Group( "Structures" );
        this.groups.add( structureGroup );
        this.bases = new Type( "Base" );
        structureGroup.addType( this.bases );
        this.towers = new Type( "Tower" );
        structureGroup.addType( this.towers );
        
        Group unitGroup = new Group( "Unit" );
        this.groups.add( unitGroup );
        this.colonists = new Type( "Colonist" );
        unitGroup.addType( this.colonists );
        this.explorers = new Type( "Explorer" );
        unitGroup.addType( this.explorers );
        this.melees = new Type( "Melee" );
        unitGroup.addType( this.melees );
        this.ranges = new Type( "Ranged" );
        unitGroup.addType( this.ranges );
        
        Group rallyGroup = new Group( "RallyPoints" );
        this.groups.add( rallyGroup );
        // change by Ross: Changed the string to "" from "Rally Point"
        this.rallyPointType = new Type( "" );
        rallyGroup.addType( this.rallyPointType );
        
        this.idToArmyGroup = new Hashtable< Integer, SelectableGroup >();
        
        this.initialUnits( startLoc );
    }
    
    /**
     * Returns an array of the various groups of game instance types.
     * 
     * @author alex kagioglu
     * @return the groups of game instance types.
     */
    public SelectableGroup[] getGroups()
    {
        SelectableGroup[] zeGroups = new SelectableGroup[ groups.size() + armyGroups.size() ];
        
        int i = 0;
        
        for ( i = 0; i < groups.size(); i++ )
        {
            zeGroups[ i ] = groups.get( i );
        }
        
        for ( int j = 0; j < armyGroups.size(); j++ )
        {
            zeGroups[ i++ ] = armyGroups.get( j );
        }
        
        return zeGroups;
    }
    
    public Location getInstLocation( SelectableInstance si )
    {
        return this.getInstLocation( (ModelInstance) si );
    }
    
    /**
     * Returns an array of the various army rally points under the user's
     * command.
     * 
     * @author alex kagioglu
     * @return an array of the player's selectable rally points.
     */
    public SelectableRallyPoint[] getRallyPoints()
    {
        return this.rallyPointList.toArray( new SelectableRallyPoint[ this.rallyPointList.size() ] );
    }
    
    /**
     * Returns an array of the various structures under the user's command.
     * 
     * @author alex kagioglu
     * @return an array of the player's selectable structures.
     */
    public SelectableStructure[] getStructures()
    {
        return this.structureList.toArray( new SelectableStructure[ this.structureList.size() ] );
    }
    
    /**
     * 
     * @author alex kagioglu
     * @param rp
     */
    private void createRallyPoint( RallyPoint rp )
    {
        this.rallyPointType.addSelectableInstance( rp );
        Type battleGroup = new Type( "BattleGroup" );
        Type reinforcements = new Type( "Reinforcements" );
        Type everyone = new Type( "Everyone" );
        Group armyGroup = new Group( "Army:" + rp.getID() );
        armyGroup.addType( battleGroup );
        armyGroup.addType( reinforcements );
        armyGroup.addType( everyone );
        this.idToArmyGroup.put( rp.getID(), armyGroup );
        rp.storeTypes( battleGroup, reinforcements, everyone );
        
        if ( rallyPointList.size() > rp.getID() )
        {
            this.rallyPointList.add( rp.getID() - 1, rp );
            this.armyGroups.add( rp.getID() - 1, armyGroup );
        }
        else
        {
            if ( rallyPointList.size() == 0 )
            {
                rallyPointList.add( rp );
                armyGroups.add( armyGroup );
            }
            else
            {
                boolean added = false;
                
                for ( int i = 0; i < rallyPointList.size(); i++ )
                {
                    SelectableRallyPoint curPoint = rallyPointList.get( i );
                    
                    if ( curPoint.getID() > rp.getID() )
                    {
                        rallyPointList.add( i, rp );
                        armyGroups.add( i, armyGroup );
                        i = rallyPointList.size();
                        added = true;
                    }
                }
                
                if ( !added )
                {
                    rallyPointList.add( rp );
                    armyGroups.add( armyGroup );
                }
                
            }
        }
    }
    
    /**
     * 
     * @author alex kagioglu
     * @param rp
     */
    private void destroyRallyPoint( RallyPoint rp )
    {
        this.rallyPointType.removeSelectableInstance( rp );
        this.armyGroups.remove( this.idToArmyGroup.get( rp.getID() ) );
        this.idToArmyGroup.remove( rp.getID() );
        
        this.rallyPointList.remove( rp );
    }
    
    @Override
    protected void addNewBaseExtras( Base b )
    {
        this.bases.addSelectableInstance( b );
        this.structureList.add( b );
    }
    
    @Override
    protected void addNewColonistExtras( Colonist c )
    {
        this.colonists.addSelectableInstance( c );
    }
    
    @Override
    protected void addNewExplorerExtras( Explorer e )
    {
        this.explorers.addSelectableInstance( e );
    }
    
    @Override
    protected void addNewMeleeExtras( Melee m )
    {
        this.melees.addSelectableInstance( m );
    }
    
    @Override
    protected void addNewRangedExtras( Ranged r )
    {
        this.ranges.addSelectableInstance( r );
    }
    
    @Override
    protected void addNewTowerExtras( Tower t )
    {
        this.towers.addSelectableInstance( t );
        this.structureList.add( t );
    }
    
    @Override
    protected void createRallyPointExtras( RallyPoint rp )
    {
        this.createRallyPoint( rp );
    }
    
    @Override
    protected void decommRallyPointExtras( RallyPoint rp )
    {
        this.destroyRallyPoint( rp );
    }
    
    protected void disbandExtras( RallyPoint rallypoint )
    {
        this.destroyRallyPoint( rallypoint );
    }
    
    protected void decommColonistExtras( Colonist c )
    {
        this.colonists.removeSelectableInstance( c );
    }
    
    protected void decommExplorerExtras( Explorer e )
    {
        this.explorers.removeSelectableInstance( e );
    }
    
    protected void decommMeleeExtras( Melee m )
    {
        this.melees.removeSelectableInstance( m );
    }
    
    protected void decommRangedExtras( Ranged r )
    {
        this.ranges.removeSelectableInstance( r );
    }
    
    protected void explorerLeaveArmy( Explorer e, RallyPoint rp )
    {
        this.explorers.removeSelectableInstance( e );
    }
    
    protected void colonistLeaveArmy( Colonist c, RallyPoint rp )
    {
        this.colonists.removeSelectableInstance( c );
    }
    
    protected void meleeLeaveArmy( Melee m, RallyPoint rp )
    {
        this.melees.removeSelectableInstance( m );
    }
    
    protected void rangedLeaveArmy( Ranged r, RallyPoint rp )
    {
        this.ranges.removeSelectableInstance( r );
    }
    
    protected void decommBaseExtras( Base b )
    {
        this.bases.removeSelectableInstance( b );
        this.structureList.remove( b );
    }
    
    protected void decommTowerExtras( Tower t )
    {
        this.towers.removeSelectableInstance( t );
        this.structureList.remove( t );
    }
}
